package cc.z8g.browser.search.suggestions

import cc.z8g.browser.BrowserApp
import cc.z8g.browser.R
import cc.z8g.browser.database.SearchSuggestion
import cc.z8g.browser.extensions.preferredLocale
import cc.z8g.browser.extensions.safeUse
import io.reactivex.rxjava3.core.Single
import org.xmlpull.v1.XmlPullParserFactory
import java.io.InputStream
import java.net.URL
import java.util.Collections

/**
 * The base search suggestions API. Provides common fetching and caching functionality for each
 * potential suggestions provider.
 */
abstract class BaseSuggestionsModel internal constructor() : SuggestionsRepository {

    abstract fun createQueryUrl(word: String): String

    @Throws(Exception::class)
    protected abstract fun parseResults(bodyStream: InputStream): List<SearchSuggestion>

    override fun resultsForSearch(rawQuery: String): Single<List<SearchSuggestion>> = Single.fromCallable {
        try {
            return@fromCallable URL(createQueryUrl(rawQuery)).openStream()?.safeUse(::parseResults)?.take(MAX_RESULTS) ?: emptyList()
        } catch (throwable: Throwable) {
            return@fromCallable Collections.emptyList()
        }
    }

    companion object {

        private const val MAX_RESULTS = 5
        private const val DEFAULT_LANGUAGE = "en"

        // Converting to a lambda results in pulling the newInstance call out of the lazy.
        @Suppress("ConvertLambdaToReference")
        internal val parser by lazy {
            XmlPullParserFactory.newInstance().apply {
                isNamespaceAware = true
            }.newPullParser()
        }

        internal val suggestionStr by lazy { BrowserApp.getApplication().getString(R.string.suggestion) }

        internal val language by lazy {
            BrowserApp.getApplication().preferredLocale.language.takeIf(String::isNotEmpty) ?: DEFAULT_LANGUAGE
        }
    }

}
